
package com.joysuch.wwyt.core.service.impl;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baidu.aip.face.AipFace;
import com.joysuch.wwyt.api.base.vo.ApiConfigVo;
import com.joysuch.wwyt.api.mh.dto.TenantBean;
import com.joysuch.wwyt.api.saferisk.BpSafeRiskJob;
import com.joysuch.wwyt.baidu.req.BaiduSample;
import com.joysuch.wwyt.bp.service.BpRiskCompanyService;
import com.joysuch.wwyt.common.constant.Constant;
import com.joysuch.wwyt.common.constant.Constant;
import com.joysuch.wwyt.config.RedisClient;
import com.joysuch.wwyt.core.bean.AddConfigBean;
import com.joysuch.wwyt.core.bean.BaseConfigBean;
import com.joysuch.wwyt.core.bean.BaseConfigTypeBean;
import com.joysuch.wwyt.core.common.util.PageDataUtils;
import com.joysuch.wwyt.core.common.util.PageDataUtils.PageDataConvert;
import com.joysuch.wwyt.core.entity.BaseConfig;
import com.joysuch.wwyt.core.entity.BaseConfigType;
import com.joysuch.wwyt.core.entity.dsl.QBaseConfig;
import com.joysuch.wwyt.core.enums.BaseConfigCodes;
import com.joysuch.wwyt.core.repository.BaseConfigDao;
import com.joysuch.wwyt.core.repository.BaseConfigTypeDao;
import com.joysuch.wwyt.core.service.BaseConfigService;
import com.joysuch.wwyt.core.support.Context;
import com.joysuch.wwyt.dzhapi.bean.ApiBaseConfigBean;
import com.joysuch.wwyt.dzhapi.dto.BuildDataDto;
import com.joysuch.wwyt.dzhapi.mapper.ApiBaseConfigMapper;
import com.joysuch.wwyt.util.StringUtil;
import com.querydsl.core.types.ExpressionUtils;
import com.querydsl.core.types.Predicate;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;

import static com.joysuch.wwyt.core.bean.BaseConfigBean.SearchBaseConfig;

/**
 * @author qingzhou
 * 2019-12-17
 */
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class BaseConfigServiceImpl implements BaseConfigService {

    private final BaseConfigDao dao;

    @Autowired
    private RedisClient redisClient;
    @Autowired
    private BaseConfigTypeDao baseConfigTypeDao;
    @Autowired
    private ApiBaseConfigMapper baseConfigMapper;

    public static AipFace client;

    @Autowired
    private BpSafeRiskJob bpSafeRiskJob;

    /**
     * 查询
     *
     * @param tenantId
     * @param orgCode
     * @param code
     * @return
     */
    @Override
    public BaseConfig findConfig(Long tenantId, String orgCode, String code) {
        return getConfig(tenantId,orgCode,code);
    }

    @Override
    public BaseConfig findFirstConfig(String code) {
        return dao.findFirstByCode(code);
    }

    public void removeBaseConfigCache(String code) {
        redisClient.removeMapKey(builderBaseConfigKey(), code);
    }

    @Override
    public BaseConfig getBaseConfigFromRedis(String code) {
        BaseConfig config = redisClient.hget(builderBaseConfigKey(), code, BaseConfig.class);
        if (config == null) {
            config = findFirstConfig(code);
            if (config == null) {
                log.error(String.format("[%s]系统参数配置为空", code));
                return null;
            }
            Long tenantId = config.getTenentId();
            String orgCode = config.getOrgCode();
            String redisKey = String.format(BaseConfigCodes.BASE_CONFIG_REDIS_KEY, tenantId, orgCode);
            redisClient.hput(redisKey, code, JSON.toJSONString(config));
        }
        return config;
    }

    private String builderBaseConfigKey() {
        Long tenantId = Context.getCurrentTenantId();
        String orgCode = Context.getCurrentOrgCode();
        return String.format(BaseConfigCodes.BASE_CONFIG_REDIS_KEY, tenantId, orgCode);
    }

    @Override
    public BaseConfig findConfig(String code) {
        Long tenantId = Context.getCurrentTenantId();
        String orgCode = Context.getCompanyCode();
        return getConfig(tenantId,orgCode,code);
    }

    private BaseConfig getConfig(Long tenantId, String orgCode, String code){
        BaseConfig config;
        String s = redisClient.get(tenantId+"_"+orgCode+"_"+code);
        if (s != null) {
            return JSONObject.parseObject(s, BaseConfig.class);
        }
        config = dao.findByTenentIdAndOrgCodeAndCode(tenantId, orgCode, code);
        redisClient.set(tenantId+"_"+orgCode+"_"+code,JSON.toJSONString(config));
        return config;
    }

    @Override
    public BaseConfig addConfig(String key, String value) {
        BaseConfig bc = new BaseConfig();
        bc.setCode(key);
        bc.setValue(value);
        bc.setTenentId(Context.getCurrentTenantId());
        bc.setOrgCode(Context.getCompanyCode());
        dao.save(bc);
        redisClient.set(bc.getTenentId()+"_"+bc.getOrgCode()+"_"+bc.getCode(),JSON.toJSONString(bc));
        return bc;
    }

    @Override
    public BaseConfig updateConfigValue(BaseConfig baseConfig, String value) {
        baseConfig.setValue(value);
        dao.save(baseConfig);
        redisClient.set(baseConfig.getTenentId()+"_"+baseConfig.getOrgCode()+"_"+baseConfig.getCode(),JSON.toJSONString(baseConfig));
        return baseConfig;
    }

    @Override
    public BaseConfig addConfig(String key, String value, Long tenentId, String companyCode) {
        BaseConfig bc = new BaseConfig();
        bc.setCode(key);
        bc.setValue(value);
        bc.setTenentId(tenentId);
        bc.setOrgCode(companyCode);
        dao.save(bc);
        return bc;
    }

    /**
     * 新增
     *
     * @param bean
     * @return
     */
    @Override
    public BaseConfig add(AddConfigBean bean) {
        BaseConfig bc = new BaseConfig();
        Long tId = Context.getCurrentTenantId();
        String cCode = Context.getCompanyCode();
        String code = bean.getCode();
        BaseConfig bcf = dao.findByTenentIdAndOrgCodeAndCode(tId, cCode, code);
        if (bcf == null) {
            bc.setCode(code);
            bc.setValue(bean.getValue());
            bc.setTenentId(tId);
            bc.setOrgCode(cCode);
            bc.setDescription(bean.getDescription());
            bc.setTypeId(bean.getTypeId());
            dao.save(bc);
            redisClient.set(bc.getTenentId()+"_"+bc.getOrgCode()+"_"+bc.getCode(),JSON.toJSONString(bc));
            return bc;
        } else {
            return null;
        }

    }

    /**
     * 修改
     *
     * @param bean
     */
    @Override
    public void updateConfig(BaseConfig bean) {
        //删除缓存
        removeBaseConfigCache(bean.getCode());
        Long id = bean.getId();
        BaseConfig oldConfig = dao.findByTenentIdAndOrgCodeAndId(Context.getCurrentTenantId(), Context.getCompanyCode(), id);
        if (oldConfig == null) {
            return;
        }
        String oldValue = oldConfig.getValue();
        checkAllowEdit(oldConfig);

        BaseConfig bc = new BaseConfig();
        bc.setId(id);
        bc.setCode(bean.getCode());
        bc.setValue(bean.getValue());
        bc.setDescription(bean.getDescription());
        bc.setTenentId(Context.getCurrentTenantId());
        bc.setOrgCode(Context.getCompanyCode());
        bc.setTypeId(bean.getTypeId());
        dao.save(bc);
        redisClient.set(bc.getTenentId() + "_" + bc.getOrgCode() + "_" + bc.getCode(), JSON.toJSONString(bc));
        updateBaiduClient(bean.getCode());
        updateRiskCompany(bean.getCode(), bean.getValue());

        // 处理特殊业务逻辑
        this.handleAfterConfigUpdate(bean, oldValue);
    }

    private void handleAfterConfigUpdate(BaseConfig bean, String oldValue) {
        try {
            // 修改人员定位配置, 需要重新订阅
            if (BaseConfigCodes.LOCATE_SERVER_URL.equals(bean.getCode()) && !StringUtil.isEmpty(bean.getValue()) && !bean.getValue().equals(oldValue)) {
                redisClient.set(Constant.LOCATE_CONFIG_UPDATE, bean.getOrgCode() + "#" + bean.getTenentId());
                log.info("========== 修改人员定位配置, 需要重新订阅服务, code: {}, oldValue: {}, newValue: {}", bean.getCode(), oldValue, bean.getValue());
            }
            // 修改隐患治理历史数据保留时长, 需要删除隐患治理数据迁移完毕的redis标识, 让系统能重新迁移存量, 停止迁移增量
            if (BaseConfigCodes.INVESTIGATE_HIS_DATA_SAVE_MONTH.equals(bean.getCode()) && !StringUtil.isEmpty(bean.getValue()) && !bean.getValue().equals(oldValue)) {
                redisClient.removeKey(Constant.MOVE_INVESTIGATE_HIS_OVER_FLAG);
                log.info("========== 修改隐患治理历史数据保留时长, 需要删除隐患治理数据迁移完毕的redis标识, code: {}, oldValue: {}, newValue: {}", bean.getCode(), oldValue, bean.getValue());
            }
        } catch (Exception e) {
            log.error("========== 修改配置时, 处理业务异常! configCode: {}, errMsg： {}", bean.getCode(), e.getMessage());
        }
    }


    /**
     * 判断更新公告
     *
     * @param code
     */
    public void updateRiskCompany(String code, String value) {
        if (BaseConfigCodes.DEFAULT_TIME_OF_ANNOUNCEMENT.equals(code)) {
            ScheduledTaskRegistrar taskRegistrar = bpSafeRiskJob.scheduledTaskRegistrar;
            taskRegistrar.destroy();
            Map<Runnable,Trigger>map=new HashMap();
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    bpSafeRiskJob.sendCompanyPromise();
                }
            };
            Trigger trigger = new Trigger() {
                @Override
                public Date nextExecutionTime(TriggerContext triggerContext) {
                    // 任务触发，可修改任务的执行周
                        String hour = "00";
                        String minute = "00";
                        String[] strings = value.split(":");
                        if (strings[0] != null) {
                            minute = strings[0];
                            hour = strings[1];
                        }
                        String startStr = "0 ";
                        String endStr = " * * ?";
                      String  cronStr = startStr + hour + " " + minute + endStr;

                    CronTrigger trigger = new CronTrigger(cronStr);
                    Date nextExec = trigger.nextExecutionTime(triggerContext);
                    return nextExec;
                }
            };
            map.put(runnable,trigger);
            taskRegistrar.setTriggerTasks(map);
            taskRegistrar.afterPropertiesSet();
        }
    }


    public void updateBaiduClient(String code){
        String appId="";
        String key="";
        String secret="";
        if (code.equals(BaseConfigCodes.BAIDU_FACE_ID_APP_ID)||code.equals(BaseConfigCodes.BAIDU_FACE_ID_APP_KEY)||code.equals(BaseConfigCodes.BAIDU_FACE_ID_APP_SECRET)) {
            BaseConfig appIdConfig = dao.findByTenentIdAndOrgCodeAndCode(Context.getCurrentTenantId(), Context.getCompanyCode(),BaseConfigCodes.BAIDU_FACE_ID_APP_ID);
            if (appIdConfig!=null && StringUtils.isNotBlank(appIdConfig.getValue())) {
                appId=appIdConfig.getValue();
            }
            BaseConfig appKeyConfig =dao.findByTenentIdAndOrgCodeAndCode(Context.getCurrentTenantId(), Context.getCompanyCode(),BaseConfigCodes.BAIDU_FACE_ID_APP_KEY);
            if (appKeyConfig!=null&& StringUtils.isNotBlank(appKeyConfig.getValue())) {
                key=appKeyConfig.getValue();
            }
            BaseConfig appSecretConfig = dao.findByTenentIdAndOrgCodeAndCode(Context.getCurrentTenantId(), Context.getCompanyCode(),BaseConfigCodes.BAIDU_FACE_ID_APP_SECRET);
            if (appSecretConfig!=null&&StringUtils.isNotBlank(appSecretConfig.getValue())) {
                secret=appSecretConfig.getValue();
            }
            client = new AipFace(appId, key, secret);

            // 可选：设置网络连接参数
            client.setConnectionTimeoutInMillis(2000);
            client.setSocketTimeoutInMillis(60000);
            BaiduSample.client=client;
            BaiduSample.APP_ID=appId;
            BaiduSample.SECRET_KEY=secret;
            BaiduSample.API_KEY=key;
        }
    }

    /**
     * 删除
     *
     * @param id
     */
    @Override
    public void delete(Long id) {
        BaseConfig bc = dao.findById(id).orElseThrow(() -> new IllegalArgumentException("配置类型不存在"));
        if (bc == null) {
            return;
        }
        checkAllowEdit(bc);
        redisClient.removeKey(bc.getTenentId()+"_"+bc.getOrgCode()+"_"+bc.getCode());
        removeBaseConfigCache(bc.getCode());
        dao.delete(bc);
    }

    /**
     * 检查是否可以修改
     *
     * @param baseConfig
     */
    private void checkAllowEdit(BaseConfig baseConfig) {
        String errorMsg = BaseConfigService.EDIT_NOT_ALLOWED_CODE_MAP.get(baseConfig.getCode());
        if (StrUtil.isNotBlank(errorMsg)) {
            throw new IllegalArgumentException(errorMsg);
        }
    }

    /**
     * 分页
     *
     * @param page
     * @return
     */
    @Override
    public Page<BaseConfigBean> findByPage(Pageable page, SearchBaseConfig condition) {
        Predicate predicate = prepareListSearchCondition(condition);
        Page<BaseConfig> data = predicate == null ? dao.findAll(page) : dao.findAll(predicate, page);
        return PageDataUtils.convertPageData(data, new PageDataConvert<BaseConfigBean, BaseConfig>() {
            @Override
            public BaseConfigBean convert(BaseConfig baseConfig) {
                BaseConfigBean bean = new BaseConfigBean();
                BeanUtils.copyProperties(baseConfig, bean);
                if (baseConfig.getTypeId() != null) {
                    Optional<BaseConfigType> obt = baseConfigTypeDao.findById(baseConfig.getTypeId());
                    if (obt.isPresent()) {
                        bean.setTypeName(obt.get().getName());
                    }
                }
                return bean;
            }
        });
    }

    /**
     * 通过类型id获取配置列表
     *
     * @param typeId
     * @return
     */
    @Override
    public List<ApiConfigVo> findByTypeId(Long typeId) {
        List<ApiConfigVo> configVos = new ArrayList<>();
        List<BaseConfig> configList = dao.findByTypeId(typeId);
        configList.forEach(each -> configVos.add(ApiConfigVo.builder().code(each.getCode()).value(each.getValue()).build()));
        return configVos;
    }

    @Override
    public BaseConfig findByTenentIdAndOrgCodeAndCode2(Long tenentId, String orgCode, String code) {
        return dao.findByTenentIdAndOrgCodeAndCode2(tenentId,orgCode,code);
    }

    @Override
    public List<BaseConfig> findConfigByList(List<TenantBean> tenantBeans, String monitorPointHttpServerUrl) {
        return baseConfigMapper.findConfigByList(tenantBeans,monitorPointHttpServerUrl);
    }

    @Override
    public List<BaseConfig> findConfigList(Long tenentId, String companyCode) {
        return dao.findAllByTenentIdAndOrgCode(tenentId, companyCode);
    }

    /**
     * 保存配置类型
     *
     * @param bean
     * @return
     */
    @Override
    public BaseConfigType saveType(BaseConfigTypeBean bean) {
        BaseConfigType type = new BaseConfigType();
        if (bean.getId() != null) {
            type = baseConfigTypeDao.findById(bean.getId()).orElseThrow(() -> new IllegalArgumentException("配置类型不存在"));
        }
        type.setName(bean.getName());
        BaseConfigType save = baseConfigTypeDao.save(type);
        return save;
    }

    /**
     * 查看配置类型
     *
     * @param id
     * @return
     */
    @Override
    public BaseConfigTypeBean findTypeById(Long id) {
        BaseConfigType type = baseConfigTypeDao.findById(id).orElseThrow(() -> new IllegalArgumentException("配置类型不存在"));
        BaseConfigTypeBean typeBean = new BaseConfigTypeBean();
        typeBean.setId(type.getId());
        typeBean.setName(type.getName());
        return typeBean;
    }

    /**
     * 获取配置类型列表
     *
     * @return
     */
    @Override
    public List<BaseConfigTypeBean> getTypeList() {
        final List<BaseConfigType> all = baseConfigTypeDao.findAll();
        List<BaseConfigTypeBean> list = new ArrayList<>();
        for (BaseConfigType type : all) {
            BaseConfigTypeBean bean = new BaseConfigTypeBean();
            bean.setId(type.getId());
            bean.setName(type.getName());
            list.add(bean);
        }
        return list;
    }

    /**
     * 删除配置类型
     *
     * @param id
     */
    @Override
    public void deleteTypeById(Long id) {
        List<BaseConfig> list = dao.findByTypeId(id);
        if (!CollectionUtils.isEmpty(list)) {
            throw new IllegalArgumentException("请先删除该类型下的所有配置");
        }
        baseConfigTypeDao.deleteById(id);
    }

    private Predicate prepareListSearchCondition(BaseConfigBean.SearchBaseConfig condition) {
        Predicate p = null;
        QBaseConfig q = QBaseConfig.baseConfig;
        if (condition.getCode() != null) {
            p = ExpressionUtils.and(p, q.code.contains(condition.getCode()));
        }
        if (condition.getValue() != null) {
            p = ExpressionUtils.and(p, q.value.contains(condition.getValue()));
        }
        if (condition.getTypeId() != null) {
            p = ExpressionUtils.and(p, q.typeId.eq(condition.getTypeId()));
        }
        if (condition.getIsRequired() != null) {
            p = ExpressionUtils.and(p, q.isRequired.eq(condition.getIsRequired()));
        }
        if (condition.getDescription() != null) {
            p = ExpressionUtils.and(p, q.description.contains(condition.getDescription()));
        }

        return p;
    }

    @Override
    public boolean checkIfSync(Long tenentId, String orgCode) {
        BaseConfig versionConfig = this.findConfig(tenentId, orgCode.substring(0, 7), BaseConfigCodes.LOCATE_VERSION);
        if (versionConfig == null || "3.x".equalsIgnoreCase(versionConfig.getValue())) {
            return false;
        }
        BaseConfig syncConfig = this.findConfig(tenentId, orgCode.substring(0, 7), BaseConfigCodes.LOCATE_SERVER_EMPLOYEE_SYNC_OPEN);
        if (syncConfig == null || "false".equalsIgnoreCase(syncConfig.getValue())) {
            return false;
        }
        return true;
    }

    @Override
    public boolean checkIfSync() {
        BaseConfig versionConfig = this.findConfig(BaseConfigCodes.LOCATE_VERSION);
        if (versionConfig == null || "3.x".equalsIgnoreCase(versionConfig.getValue())) {
            return false;
        }
        BaseConfig syncConfig = this.findConfig(BaseConfigCodes.LOCATE_SERVER_EMPLOYEE_SYNC_OPEN);
        if (syncConfig == null || "false".equalsIgnoreCase(syncConfig.getValue())) {
            return false;
        }
        return true;
    }

    @Override
    public boolean checkApiIfSync(BuildDataDto dto) {
        ApiBaseConfigBean versionConfig = baseConfigMapper.findConfig(dto.getTenentId(), dto.getOrgCode(), BaseConfigCodes.LOCATE_VERSION);
        if (versionConfig == null || "3.x".equalsIgnoreCase(versionConfig.getValue())) {
            return false;
        }
        ApiBaseConfigBean syncConfig = baseConfigMapper.findConfig(dto.getTenentId(), dto.getOrgCode(), BaseConfigCodes.LOCATE_SERVER_EMPLOYEE_SYNC_OPEN);
        if (syncConfig == null || "false".equalsIgnoreCase(syncConfig.getValue())) {
            return false;
        }
        return true;
    }

    @Override
    public List<Map<String,Object>> selectTenentIdAndOrgCodes(String url , String token) {
        return baseConfigMapper.selectTenentIdAndOrgCodes(url,token);
    }
}
